#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>


#define TRUE  1
#define FALSE 0


struct command_save{
	char *name;
	int argc;
	char *argv[64];
};			//명령어 저장용




int isNeedIOChange(char **);
int parse_command(char *strCommand, struct command_save * stCommand);
void print_prompt();
void readCommand(char *);
int parsePath(char **);
int isNoWait(char **);
void __kill(char *argv);

////////////////////////////////////////////////////////////////
int main()
{
char commandLine[50];
struct command_save command;
char pathv[256][256];
//parsePath((char**)pathv);
int pid1, pid2, fid;
int i, tmp;
int pipeID[2];
int noWait;
int status;
int pos;
int issig;

while(1){
   noWait = FALSE;
   print_prompt();
   readCommand(commandLine);

   if(strcmp(commandLine, "") == 0 ) continue;
   if(strcmp(commandLine, "exit") == 0) break;
	
   parse_command(commandLine, &command);
/////////////////////////////////////////////////////////////
   if (strcmp(command.argv[0], "2cd") == 0) 
	{
	if (chdir(command.argv[1]) != 0)
	     printf ("'%s' 디렉토리를 찾을 수 없습니다.  \n", command.argv[1]);
	else 
	     printf ("directory changed.\n");
	     continue;
	}
///////////////////////////////////////////////////cd구현
if (strcmp(command.argv[0], "2cat") == 0) {
	
int i;
int state;
char buffer[200];


for(i=1 ; i<2 ;i++)
{
	FILE *file = fopen(command.argv[1],"r"); 
	if(file == NULL){ 
		printf("%s : No such file or directory\n",command.argv[1]);
		continue;
	}

	while(1)
	{
	fgets(buffer,sizeof(buffer),file); //버퍼 사이즈에 받은 파일
	if(feof(file) != 0) //file을 이용하여 FILE의 구조체 변수를참조한다
	break;	
	fputs(buffer,stdout);
	}
	state = fclose(file); //파일 종결
	if(state!=0){ // 숫자가 0이 아니면 에러
	printf("file close error!\n");
	return 1;
	}
	
}

	continue;
}
///////////////////////////////////////////소스 결합 cat 기능 구현

if (strcmp(command.argv[0], "2ls") == 0) {
 DIR *dirp;
 struct dirent *dentry;
 int lnt=0;

 if((dirp=opendir("."))==NULL) 
  exit(1);

 while(dentry=readdir(dirp)){ 

  if(dentry->d_ino!=0 && dentry->d_name[0] !='.'){ 
   printf("%-15s",dentry->d_name); 
   lnt++;
   if(lnt>=5){ 
     printf("\n");
     lnt=0;
   }
  }
 }
 printf("\n");
 closedir(dirp); 
continue;
}
///////////////////////////////////////ls기능 구현 
if (strcmp(command.argv[0], "2cp") == 0) {
    char *cmd1;
    char *cmd2;
 
    cmd1 = command.argv[1]; //file name
    cmd2 = command.argv[2]; //copy file name
 
    char block[BUFSIZ];
    int in, out;
    int nread;
 
    in = open(cmd1,O_RDONLY); //원본파일
    out = open(cmd2, O_WRONLY| O_CREAT, S_IRUSR| S_IWUSR);//만들파일
    nread = read(in,block,sizeof(block)); //읽은만큼 nread가 올라가고
    write(out,block,nread);          //read만큼 쓴다.
    continue;
 }
////////////////////////////////////////copy 구현




if (strcmp(command.argv[0], "2pwd") == 0) {
    char buffer[256];
    if(getcwd(buffer,256)==NULL) //getcwd 현재 작업 디렉토리 얻기 함수
    exit(1);
    printf("%s\n",buffer);
continue;
 }

////////////////////////////////////현재디렉토리 구현
if(strcmp(command.argv[0], "2ps") == 0) {
                  
        DIR *dir;
         struct dirent *entry;
         struct stat fileStat;
         int pid;
         char cmdLine[256];
         char tempPath[256];

        FILE *srcFp;
         printf("[pid]   directory\n");
         dir = opendir("/proc");
         if(dir == NULL)
                 return 0;
         while ((entry = readdir(dir)) != NULL) { //proc directory 순차검색
                lstat(entry->d_name, &fileStat);
                 if (!S_ISDIR(fileStat.st_mode))
                         continue;
                 pid = atoi(entry->d_name);
                 if (pid <= 0)
                         continue;
                 sprintf(tempPath, "/proc/%d/cmdline", pid); //cmdline : 프로세스의 이름

                srcFp = fopen(tempPath, "r");
                 memset(cmdLine,'\0',sizeof(cmdLine));
                 fgets(cmdLine,256,srcFp);
                 fclose(srcFp);

                if(cmdLine[0] != '\0')
                         printf("[%d]\t%s\n", pid, cmdLine);
         }
         closedir(dir);
	continue;
 }

/*
 DIR *dir;            //  /proc/pid/ 를 가리킬 DIR* 변수
 struct dirent *entry;  // 각 파일의 inode를 통해 파일을 선택할 dirent 구조체
 struct stat fileStat;  // 파일의 정보를 담는 구조체
 
 int pid;      // 프로세스는 /proc 디렉토리에 자신의 pid로 파일을 담아 둡니다.
 char cmdLine[256];
 char tempPath[256];
 
 printf("Processes Info\n");
 dir = opendir("/proc");   
 while ((entry = readdir(dir)) != NULL) { 
       lstat(entry->d_name, &fileStat);
       if (!S_ISDIR(fileStat.st_mode))  
              continue;        // 프로세스는 /proc에 자신의 pid로 디렉토리를
                               // 만드는 점을 안다면 이해하실거라 생각합니다.
       pid = atoi(entry->d_name);// 프로세스(디렉토리)인것을 확인하면, 숫자로 반환한다.
         if (pid <= 0) continue;     // 숫자가 아니라면 다시 continue;
       sprintf(tempPath, "/proc/%d/cmdline", pid); //cmdline::프로세스 이름이 적힌파일
       getCmdLine(tempPath, cmdLine);// /proc/pid/cmdline에서 프로세스의 이름을
                                   // 가져오는 함수로 보냅니다.
       printf("[%d] %s\n", pid, cmdLine);
     }
     closedir(dir);
continue;
*/

 ////////////////////////////////////////////////////ps기능

if(strcmp(command.argv[0], "2mkdir") == 0){
int opt;
  int ch;
  char conin = 'h'; //문자형 이름을 h로 선언
  opt = getopt(command.argc, command.argv, "m:"); //프로그램을 실행할때 옵션주기
  if(opt == 'm') //옵션이 m이면 
  mkdir(command.argv[2], ch); //새로운 디렉토리의 허가모드로 지정한 모드로 설정
  else
  mkdir(command.argv[1], 0755); //부모 디렉토리가 존재하지 않은 경우 자동 생성한다. 

continue;
}
////////////////////////////////////////////////mkdir
if(strcmp(command.argv[0], "2rmdir") == 0) {
int rmdir(const char *path); //삭제될 디렉토리의이름은path 매개변수로 지정한다.
if(rmdir(command.argv[1])){
switch (errno){ //예외처리상황에대해서다음과같은상수를가진다. (errno.h)
  case ENOTEMPTY : printf("디렉토리가 비어있지 않습니다.\n");break;     
  case ENOENT : printf("해당 디렉토리가 존재하지 않습니다.\n");break;   
  case EACCES : printf("해당 디렉토리에 대한 권한이 없습니다.\n");break;
  default : printf("알수 없는 오류입니다.\n");
        }
  }
continue;
}
////////////////////////////////////////////////rmdir 
if(strcmp(command.argv[0], "2mv") == 0) {
 int rtn;

    if(command.argc != 3)
    {
        printf("사용방법 : cmd file1 file2\n");
        exit(0);
    }
    rtn = link(command.argv[1], command.argv[2]);
    if(rtn == -1)
    {
        perror(command.argv[1]);
        exit(0);
    }
    rtn = unlink(command.argv[1]);
    if(rtn == -1)
    {
        perror(command.argv[1]);
        exit(0);
    }
    continue;
}


/////////////////////////////////////////////////////mv
if(strcmp(command.argv[0], "2kill")==0){
        int signo;
	int pid;

	signo = 9;
	pid = atoi(command.argv[1]);

	// 해당 pid에 signo 신호를 보냄
	if(kill(pid, signo) == -1){
		perror("Failed to send signal");
	}
	continue;
}
//////////////////////////////////////////////kill



if(strcmp(command.argv[0], "2rm")==0){
       if(unlink(command.argv[1])) {    
       switch (errno) {        
                  case ENOENT :
                       printf("해당 파일이 존재하지 않습니다.\n");
                        break;      
                  case EACCES :
                       printf("접근이 허용된 파일이 아닙니다.\n");
                        break; 
                  case ENOTDIR :
                       printf("디렉토리 입니다.\n");
                        break;    
                  case EROFS :
                       printf("읽기전용의 파일입니다.\n");
                        break;    
                  default : printf("error.\n"); 
               }
	}
	continue;
}
//////////////////////////////////////////////////////rm 기능 











noWait = isNoWait(command.argv);

if ((tmp = isNeedIOChange(command.argv)) != 0) 
	{
	switch(command.argv[tmp][0]) 
	{
	case '>':	
	if ( (pid1 = fork()) == 0 ) {
	fid = open(command.argv[tmp+1], O_WRONLY | O_CREAT, 0777);
		close(1); 
		dup(fid); 
		execvp(command.argv[0], command.argv);
		close(fid);
		exit(1);
	} 		
	break;

	case '<':
	if((pid1 = fork()) == 0) {
	fid = open(command.argv[tmp+1], O_RDONLY);
	close(1);
	dup(fid);
	execvp(command.argv[0], command.argv);
	close(fid);
	exit(1);
	}
	break;
	
	case '|':
	pipe(pipeID);
	if((pid1 = fork()) == 0){

	close(1);
	dup(pipeID[1]);
	execvp(command.argv[0], command.argv);
	exit(1);
	}
	else if((pid2 = fork()) == 0){

	close(0);
	dup(pipeID[0]);
	execvp(command.argv[tmp+1], &command.argv[tmp+1]);
	exit(1);
	}
	if (noWait != TRUE)
	wait(&status);
	break;
}
}
else {	
	if ((pid1 = fork()) == 0) { 
	execvp(command.argv[0], command.argv);	
	exit(1);
	}
}


if (noWait != TRUE) 
	wait(&status);



	}
}


//////////////////////////////////////////////////
//화면에 현재 경로와 스트링을 출력
/////////////////////////////////////////////////
void print_prompt()
{
    char promptString[512] = "(SHELL) :";
    char buffer[512];
    getcwd(buffer, 512);

    strcat(promptString, buffer);

    printf("-%s $ ", promptString);
}

/////////////////////////////////////////////////////////////////////
//커맨드를 입력받아 버퍼에 넣는 함수
/////////////////////////////////////////////////////////////////////

void readCommand(char *buffer){
	gets(buffer);
}
//////////////////////////////////////////////////////////////////////
//입력받은 커맨드를 분석하여 여러개의 아규먼트로 분리하는 함수
//아규먼트를 분리하여 command_save에 저장
//리턴값 : 성공여부에 대한 0 또는 1 값
//////////////////////////////////////////////////////////////////////

int parse_command(char *strCommand, struct command_save *stCommand)
{
   int count = 0;
   int posi = 0;
   int s_posi = 0;
   int i;
   char *argv[64];
   char is_command = FALSE;

   argv[count] = malloc(64);
   strcat(strCommand, " ");

   while(strCommand[posi] != '\0'){
	if(strCommand[posi] == ' ')
 	  if(is_command == FALSE){
	     posi++;
	     continue;
	  }
	else{
		argv[count][s_posi]= '\0';
		s_posi = 0;
		posi++;
		count++;
		argv[count] = malloc(64);
		is_command = FALSE;
		continue;
	    }
	argv[count][s_posi++] = strCommand[posi++];
	is_command = TRUE;
   }
   stCommand->argc = count;
   free(argv[count]);

   for(i=0; i < stCommand->argc; i++)
	stCommand->argv[i] = argv[i];

   stCommand->argv[stCommand->argc] = NULL;

   return 1;

}

/*
int parsePath(char *dirs[]) {
	char *pathEnvVar;
	char *thePath;
	int i;
	int pos = 0;
	int spos = 0;
	int cnt = 0;

	for (i=0; i<256; i++)
		dirs[i] = NULL;

	pathEnvVar = (char*) getenv("PATH");
	thePath = (char *)malloc(strlen(pathEnvVar) + 1);
	strcpy(thePath, pathEnvVar);

	dirs[cnt] = (char *)malloc(256);
	while (thePath[pos] != '\0') {
		if (thePath[pos] == ':') {
			dirs[cnt][spos++] = '/';
			dirs[cnt++][spos] = '\0';
			dirs[cnt] = (char *)malloc(256);
			spos = 0;
			pos++;
			continue;
		}
		dirs[cnt][spos++] = thePath[pos++];
	}
	dirs[cnt][spos++] = '/' ;
	dirs[cnt++][spos] = '\0' ;

	return 1;
}
*/

/////////////////////////////////////////////////////////
//argv를 검색하여 > < | 문자가 사용되었는지 검사
//리턴값 : > < | 문자가 사용된 아큐먼트의 배열값
////////////////////////////////////////////////////////


int isNeedIOChange(char ** argv) {
	int i;
	for (i=0;i<256;i++) {	
		if (argv[i] == NULL) break;	
		if (strcmp(argv[i],"<") == 0) return i;
		if (strcmp(argv[i],">") == 0) return i;
		if (strcmp(argv[i],"|") == 0) return i;
	}
	return 0;
}



////////////////////////////////////////////////////
//argv를 검색해서 &문자가 사용되었는지 검사
//리턴값 : &문자가 사용되었는지에 대한 TURE, FALSE값
////////////////////////////////////////////////////

int isNoWait(char ** argv) {
	int i,len;

	len = strlen(argv[0]);
	if (argv[0][len-1] == '&') {
		argv[0][len-1] = 0;
		return TRUE;
	}

	for (i=0;argv[i] != NULL;i++);
	len = strlen(argv[i-1]);
	if (argv[i-1][len-1] == '&') {
		argv[i-1][len-1] = 0;
		return TRUE;
	}

	return FALSE;
}













int getCmdLine(char *file, char *buf) {
     FILE *srcFp;
     int i;
     srcFp = fopen(file, "r");  //   /proc/pid/cmdline에 이름이 있습니다.
    memset(buf, 0, sizeof(buf));
     fgets(buf, 256, srcFp);
     fclose(srcFp);
 }
 




